home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d20 / msgq160s.arc / MSGBASE.C < prev    next >
Text File  |  1991-10-26  |  19KB  |  756 lines

  1. /*
  2.  * MSGBASE.C - Message handling
  3.  *
  4.  * Msged/Q message editor for QuickBBS  Copyright 1990 by P.J. Muller
  5.  *
  6.  */
  7.  
  8. #include <stdlib.h>
  9. #include <io.h>
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <string.h>
  13. #ifdef __MSC__
  14. #include <sys/types.h>
  15. #endif
  16. #include <sys/stat.h>
  17. #include <fcntl.h>
  18. #include <time.h>
  19.  
  20. #include "msged.h"
  21. #include "p2c.h"
  22. #include "screen.h"
  23. #include "qmsgbase.h"
  24.  
  25. #include <assert.h>
  26.  
  27. #ifdef EIDS
  28. #include "eidcrc.h"
  29. #endif
  30.  
  31. #define TEXTLEN 200        /* changed when added * Origin scanning */
  32.  
  33. static BOOLEAN checkrecvd(MSG *m);
  34. static int tputs(char *s, char **buf);
  35. static void remap(MSG *m);
  36. static void stripline(char *s);
  37. static void addid(MSG *m, ADDRESS ofrom /*, char *origtext*/ );
  38.  
  39. /*
  40.  * Display activity light
  41.  */
  42.  
  43. void working(BOOLEAN on)
  44. {
  45.   int x,y;
  46.   static char state = 1;
  47.  
  48.   x = wherex();  y = wherey();
  49.   gotoxy(maxx,1);
  50.   if (on)
  51.     bputc(state = state ^ 3);
  52.   else
  53.     bputc(' ');
  54.   gotoxy(x,y);
  55.  
  56. } /* working */
  57.  
  58. /*
  59.  * Scan the message for kludge lines and set the addressing fields
  60.  * Sets m->to and m->from
  61.  * Note:  m->*.domain de-allocated if not NULL
  62.  * In msged there was something about 'thread' here which I removed
  63.  *
  64.  * Addressing information comes from the last INTL, DOMAIN or MSGID that
  65.  * was found.  If none was found, looks in the Origin line for the address.
  66.  * If no origin line was found, use the address from the message header and
  67.  * a FMPT line if present.
  68.  */
  69.  
  70. static void parsekludge(MSG *m, BUFFER *msg)
  71. {
  72. #ifdef EIDS
  73.   EID seid;                /* EID */
  74. #endif
  75.   MSGID msgid;
  76.   char dst[31], src[31], ddom[31], sdom[31];
  77.   int  fmpt, topt;
  78.   LINE *t;                /* used for scanning text */
  79.  
  80.   if (m->to.domain != NULL)        /* this could be dangerous */
  81.     ptrfree(m->to.domain);        /* if m.to = thisnode was done */
  82.  
  83.   if (m->from.domain != NULL)
  84.     ptrfree(m->from.domain);
  85.  
  86.   m->from.zone = m->header.origzone;
  87.   m->from.net = m->header.orig_net;
  88.   m->from.node = m->header.orig;
  89.   m->from.point = 0;
  90.   m->from.domain = NULL;
  91.  
  92.   m->to.zone = m->header.destzone;
  93.   m->to.net = m->header.dest_net;
  94.   m->to.node = m->header.dest;
  95.   m->to.point = 0;
  96.   m->to.domain = NULL;
  97.  
  98. #ifdef EIDS
  99.   memset(&(m->eid),0,sizeof(m->eid));
  100.   memset(&seid,0,sizeof(seid));
  101. #endif
  102.   memset(&msgid,0,sizeof(msgid));
  103.  
  104.   *dst = *src = EOS;            /* clear all the scan variables */
  105.   fmpt = topt = 0;
  106.   memset(ddom,0,sizeof(ddom));
  107.   memset(sdom,0,sizeof(sdom));
  108.   memset(dst,0,sizeof(dst));
  109.   memset(src,0,sizeof(src));
  110.  
  111. /* now scan for kludge lines */
  112.  
  113.   t = msg->first;            /* first line of text */
  114.  
  115.   while (t != NULL) {
  116.     msg->last = t;            /* set last line */
  117.  
  118.     if ((t->text != NULL) && (*(t->text) == '\01')) {    /* is hidden line */
  119.       sscanf(t->text, "\01FMPT %i\n", &(fmpt));
  120.       sscanf(t->text, "\01TOPT %i\n", &(topt));
  121.       sscanf(t->text, "\01INTL %30s %30s\n", dst, src);
  122.       sscanf(t->text, "\01DOMAIN %30s %30s %30s %30s\n",ddom,dst,sdom,src);
  123.       sscanf(t->text, "\01MSGID: %30s %X\n", src, &(msgid.st));
  124. #ifdef EIDS
  125.       sscanf(t->text, "\01EID: %x %X", &(seid.crc), &(seid.st));
  126. #endif
  127.     } /* if */
  128.  
  129.     if (strncmp(" * Origin: ",t->text,10) == 0) {    /* origin line */
  130.  
  131.       if (*src == EOS) {        /* no source found yet */
  132.     char line2[TEXTLEN];        /* temporary origin line from msg */
  133.     char *l;
  134.  
  135.     strcpy(line2,t->text);
  136.  
  137.     if ((strchr(line2,'\n') == NULL) && (t->next != NULL))
  138.       strcat(line2,t->next->text);    /* origin line was split */
  139.  
  140.     if ((l = strrchr(line2,'(')) != NULL) {
  141.       char *m;
  142.       while (!isdigit(*l) && (*l != EOS)) l++;
  143.       m = src;
  144.       while ((*l != ')') && (m-src < 30))
  145.         *m++ = *l++;
  146.       *m = EOS;        /* copy to src */
  147.     } /* if */
  148.       } /* if */
  149.  
  150.     } /* if */
  151.  
  152.       t = t->next;            /* step on */
  153.   } /* while */
  154.  
  155.   if (*src != EOS)            /* INTL or DOMAIN line found */
  156.     m->from = parsenode(src);
  157.  
  158.   if (*dst != EOS)
  159.     m->to = parsenode(dst);
  160.  
  161.   if (fmpt && (m->from.point == 0))    /* if we already know the point */
  162.     m->from.point = fmpt;        /*   number don't add it */
  163.  
  164.   if (topt && (m->to.point == 0))
  165.     m->to.point = topt;
  166.  
  167. #ifdef EIDS
  168.   if (seid.st != 0)        /* found EID */
  169.     m->eid = seid;
  170. #endif
  171.  
  172.   if (msgid.st != 0)        /* found MSGID */
  173.     m->msgid = msgid;
  174.  
  175.   if (*ddom != EOS) {
  176.     m->to.domain = strdup(ddom);
  177.     assert(m->to.domain);
  178.   } /* if */
  179.  
  180.   if (*sdom != EOS) {
  181.     m->from.domain = strdup(sdom);
  182.     assert(m->from.domain);
  183.   } /* if */
  184.  
  185. } /* parsekludge */
  186.  
  187. /*
  188.  * Read a message and set the addressing fields
  189.  * Text is read into global 'msgbuf'
  190.  * Return TRUE if ok
  191.  */
  192.  
  193. BOOLEAN readmsg(MSG *m, int msgnum)
  194. {
  195.   char *text;
  196.  
  197.   clearbuffer(&msgbuf);                /* clear global msg buffer */
  198.  
  199.   if (!readheader(msgnum, &m->header))        /* read the header */
  200.     return FALSE;
  201.  
  202.   if (arealist[area].netmail && protectnet &&        /* protect netmail */
  203.      (strnicmp(username, m->header.from, strlen(username)) != 0) &&
  204.      (strnicmp(username, m->header.to, strlen(username)) != 0)) {
  205.  
  206.     if ((text = strdup(" * Private\r")) == NULL)
  207.       return FALSE;
  208.  
  209.   } else
  210.   if ((text = readtext(&m->header)) == NULL) {     /* read the text */
  211.     text = strdup(" * Read Error\r");        /* was too big or something */
  212.     if (text == NULL)
  213.       return FALSE;
  214.   }
  215.  
  216.   if (*text == EOS) {                /* read an empty message */
  217.     ptrfree(text);
  218.     text = strdup(" * Empty message\r");
  219.   } /* if */
  220.  
  221.   msgbuf = buffer(&text);
  222.   ptrfree(text);
  223.   if (msgbuf.first == NULL)
  224.     return FALSE;                /* maybe TRUE? */
  225.  
  226.   parsekludge(m, &msgbuf);        /* fill in addressing fields */
  227.  
  228.   if (!checkrecvd(m)) return FALSE;    /* set 'received' bit if neccessary */
  229.  
  230.   if (msgnum > highmsgread)
  231.     highmsgread = msgnum;
  232.  
  233.   return TRUE;
  234.  
  235. } /* readmsg */
  236.  
  237. /*
  238.  * Set received bit
  239.  * Return TRUE if ok
  240.  */
  241.  
  242. static BOOLEAN checkrecvd(MSG *m)
  243. {
  244.   int fmsg;
  245.   char *s = NULL, *t = NULL;
  246.   BOOLEAN result = FALSE;
  247.  
  248.   if (m->header.bits.is_rcvd)        /* already received! */
  249.     return TRUE;
  250.  
  251.   if ((fmsg = filemsg(m->header.msgnum)) == -1)
  252.     return FALSE;
  253.  
  254.   if ((s = strdup(username)) == NULL)
  255.     goto leave;
  256.   if ((t = (char *) calloc(1, sizeof(m->header.to)+1)) == NULL)
  257.     goto leave;
  258.   memcpy(t, m->header.to, sizeof(m->header.to)+1);
  259.  
  260.   if (memicmp(s, t, strlen(s)) == 0) {
  261.     m->header.bits.is_rcvd = 1;
  262.     if (!writeheader(&m->header))
  263.       goto leave;
  264.     if (!writetoidx(fmsg,"* Received *"))
  265.       goto leave;
  266.   } /* if */
  267.  
  268.   result = TRUE;
  269. leave:
  270.   ptrfree(s);
  271.   ptrfree(t);
  272.  
  273.   return(result);
  274. } /* checkrecvd */
  275.  
  276. /*
  277.  * Do the addressing of the message before writing
  278.  */
  279.  
  280. static void remap(MSG *m)
  281. {
  282. #ifdef GATE
  283.   int i;
  284. #endif
  285.  
  286.   m->header.dest_net = m->to.net;
  287.   m->header.dest = m->to.node;
  288.   m->header.destzone = m->to.zone;
  289.  
  290.   /*
  291.    * we really only want to remap crash mail if a point...  the boss
  292.    * node can (and probably will) handle remapping the rest
  293.    */
  294.  
  295.   if ((m->from.point) && (pointnet != 0)) {    /* is a point */
  296.  
  297.     if (strnicmp(m->header.to,"Areafix",7) == 0) {    /* to Areafix */
  298.       m->from.net = pointnet;
  299.       m->from.node = m->from.point;
  300.     } /* if */
  301. /* should remove this if FMPT is desired (only netmail really) */
  302.     if (!arealist[area].netmail)
  303.       m->from.point = 0;    /* no .n number, i.e. now 4921/4.0 or 7102/1.0 */
  304.   } /* if */
  305.  
  306.   m->header.origzone = m->from.zone;
  307.   m->header.orig_net = m->from.net;
  308.   m->header.orig = m->from.node;
  309.  
  310.   /*
  311.    * don't zone gate a message if it's going somewhere in your own zone
  312.    */
  313.  
  314. #ifdef GATE
  315.   if ((thisnode[CurAKA].zone != m->to.zone) && (!m->header.is_crash) && (arealist[area].netmail) && ((gate & GZONES) == GZONES)) {
  316.       m->header.dest_net = thisnode[CurAKA].zone;
  317.       m->header.dest = m->to.zone;
  318.   } /* if */
  319.  
  320.   if (((m->to.domain != NULL) && (m->from.domain != NULL)) &&
  321.        (stricmp(m->to.domain,m->from.domain)) && ((gate & GDOMAINS) == GDOMAINS))
  322.     for (i = 0; i < domains; i++)
  323.       if (stricmp(domain_list[i].domain,m->to.domain) == 0) {
  324.     m->header.dest = domain_list[i].node;
  325.     m->header.dest_net = domain_list[i].net;
  326.       }
  327. #endif
  328. } /* remap */
  329.  
  330. /*
  331.  * Search through MSGTOIDX.BBS for name
  332.  * Starts at msgnum and stops at last message in board
  333.  * Returns msgnum found or -1 if error or not found
  334.  */
  335.  
  336. int searchto(BYTE board, int msgnum, char *name)
  337. {
  338.   int fmsg, old;
  339.   char *get;
  340.  
  341.   for (;;) {
  342.     if ((fmsg = filemsg(msgnum)) == -1) return(-1);
  343.     if ((get = readtoidx(fmsg)) == NULL) return(-1);
  344.  
  345.     if (strnicmp(name,get,strlen(name)) == 0)
  346.       return(msgnum);
  347.  
  348.     if ((msgnum = msgnext(board,old = msgnum)) == old)
  349.       return(-1);        /* last message */
  350.   } /* forever */
  351. } /* searchto */
  352.  
  353. /*
  354.  * Mutilate bad lines without changing their length
  355.  */
  356.  
  357. static void stripline(char *s)
  358. {
  359.   char *p;
  360.  
  361.   while ((p = strstr(s,"SEEN-BY:")) != NULL)
  362.     p[0] = '!';            /* comes out as "!EEN-BY:" */
  363.  
  364.   for (p=s;  *p != EOS;  p++)
  365.     if (*p == '\01')
  366.       *p = '@';
  367.  
  368. } /* stripline */
  369.  
  370. /*
  371.  * Read the MSGID kludge line contents
  372.  * Return a pointer to a static buffer or NULL if nothing
  373.  */
  374.  
  375. static char *readkludge(int num)
  376. {
  377.   MSGHEADER header;
  378.   char *text, *msgid;
  379.   static char buf[101];
  380.  
  381.   buf[0] = EOS;
  382.  
  383.   if (!readheader(num, &header))
  384.     return NULL;
  385.  
  386.   if (header.numrecs == 0)
  387.     return NULL;
  388.  
  389.   header.numrecs = min(header.numrecs, 2);    /* 2 blocks max */
  390.  
  391.   if ((text = readtext(&header)) == NULL)     /* read the text */
  392.     return NULL;
  393.  
  394.   if ((msgid = strstr(text,"\01MSGID:")) != NULL)
  395.     if (sscanf(msgid, "\01MSGID: %100[^\r\n\215]", &buf[0]) == 1)
  396.       msgid = buf;
  397.     else
  398.       msgid = NULL;
  399.  
  400.   ptrfree(text);
  401.   return msgid;
  402. } /* readkludge */
  403.  
  404. /*
  405.  * Add the ^a kludges
  406.  */
  407.  
  408. static void addid(MSG *m, ADDRESS ofrom /*, char *origtext*/ )
  409. {
  410.   char buffer[TEXTLEN];
  411. #ifdef EIDS
  412.   EID rid;
  413.   memset(&rid,0,sizeof(rid));
  414. #endif
  415.  
  416. #ifdef PID        /* JoHo's Baby */
  417.   if (arealist[area].netmail) {
  418.     sprintf(buffer, "\01PID: %s %s\n", PID, PIDVER);    /* serial # opt. */
  419.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  420.   }
  421. #endif
  422.  
  423.   if (msgids) {
  424.     static BYTE counter=0;
  425.     time_t t;
  426.  
  427.     if (m->header.reply != 0) {        /* message is a reply */
  428.       char *replyid = readkludge(m->header.reply);
  429.       if (replyid != NULL) {
  430.     sprintf(buffer,"\01REPLY: %s\n", replyid);
  431.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  432.       } /* if */
  433.     } /* if */
  434.  
  435.     (void)time(&t);    /* could use time from message too */
  436.     t = (t<<4) | (counter++ & 15);    /* for more than message/s (cc:) */
  437.  
  438.     sprintf(buffer,"\01MSGID: %s %lx\n", formaddr(ofrom,Z_A|N_A|P_O|D_O),t);
  439.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  440.   } /* if */
  441.  
  442. /* Add EID line here */
  443.  
  444. #ifdef EIDS
  445.   if ((eids) && (arealist[area].echomail)) {
  446.     m->eid.crc = eidcrc(origtext, m->header.from, m->header.to, m->header.subj);
  447.     m->eid.st  = dostime(m->header.posttime,m->header.postdate,TRUE);
  448.  
  449.     if (m->eid.st != 0) {        /* EID computed ok */
  450.       if (rid.st != 0) {
  451.     sprintf(buffer, "\01EID:%04x %08lx %04x %08lx\n", m->eid.crc,
  452.         m->eid.st, rid.crc, rid.st);
  453.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  454.       } else {
  455.     sprintf(buffer, "\01EID:%04x %08lx\n", m->eid.crc, m->eid.st);
  456.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  457.       } /* else */
  458.     } /* if */
  459.   } /* if */
  460. #endif
  461.  
  462. } /* addid */
  463.  
  464. /*
  465.  * Format the origin line
  466.  * The buffer must be big enough (say char[200]) to accept the origin line
  467.  */
  468.  
  469. void formatorigin(char *buffer, char *origin, ADDRESS thisnode)
  470. {
  471.   if (origin == NULL)
  472.     origin = username;
  473.   sprintf(buffer, " * Origin: %s (%s)\n", origin, formaddr(thisnode,Z_A|N_A|P_O|D_O));
  474. } /* formatorigin */
  475.  
  476. /*
  477.  * Write the current message from the global text buffer
  478.  * Add origin line and remap the message
  479.  * Return TRUE if ok
  480.  *
  481.  *  Steps for writing a QuickBBS message:
  482.  *
  483.  *  * Search MSGIDX to see if message number already exists.
  484.  *  * Modify or add receiver's name to MSGTOIDX
  485.  *  * if new message: highmsg++, totalactive++, activemessages++ in MSGINFO
  486.  *  * Modify or add msgnum and board to MSGIDX
  487.  *  * if more text than old message, add text to end of MSGTXT, else overwrite
  488.  *  * change startrec and numrec fields and write to MSGHDR
  489.  *
  490.  */
  491.  
  492. int writemsg(MSG *m)
  493. {
  494.   char buffer[TEXTLEN];
  495.   LINE *t;
  496.   BOOLEAN newtear = (arealist[area].echomail);
  497.   BOOLEAN neworig = newtear;
  498.   char *textbuf = NULL;        /* temporary buffer for text */
  499.   BOOLEAN result = FALSE;
  500. #ifdef EIDS
  501.   char *origtext = NULL;    /* origin line text */
  502. #endif
  503.   ADDRESS ofrom = m->from;
  504.  
  505.   remap(m);            /* change addressing */
  506.  
  507. /* remove the current kludge lines */
  508.  
  509.   t = msgbuf.first;        /* in global msgbuf */
  510.   while (t != NULL) {
  511.     if (strip && (t->text[0] == '\01')) { /* remove current hidden lines */
  512.       LINE *temp = t;
  513.       t = t->next;        /* step to next line */
  514.       deleteline(&msgbuf,temp);    /* and delete current line */
  515.       continue;
  516.     } /* if */
  517.  
  518.     stripline(t->text);        /* mutilate the rest */
  519.  
  520.     if (newtear)
  521.       newtear = (strncmp(t->text,"--- ",4) != 0);
  522.  
  523.     if (neworig) {
  524.       neworig = (strncmp(t->text," * Origin: ",11) != 0);
  525. #ifdef EIDS
  526.       if (!neworig)
  527.     origtext = (t->text+11);    /* old origin line */
  528. #endif
  529.     } /* if */
  530.  
  531.     t = t->next;
  532.   } /* while */
  533.  
  534. /* now add new tearline and/or origin line */
  535.  
  536.   if (newtear) {
  537.     sprintf(buffer, "\n\n--- %s\n", PID " " PIDVER);
  538.     (void)insertline(&msgbuf, NULL, buffer);
  539.   }
  540.  
  541.   if (neworig) {                /* add origin line */
  542.     char *corigin = origin;            /* default origin line */
  543.  
  544.     if (!override)
  545.       if (arealist[area].areaorig != NULL)    /* from ECHOORIG.CTL */
  546.     corigin = arealist[area].areaorig;
  547.  
  548.     formatorigin(buffer, corigin, thisnode[CurAKA]);
  549.  
  550.     t = insertline(&msgbuf, NULL, buffer);
  551. #ifdef EIDS
  552.     origtext = (t->text+11);
  553. #endif
  554.   } /* if */
  555.  
  556. #ifdef GATE
  557.   if ((m->from.zone != m->to.zone) && (arealist[area].netmail)) {
  558.     sprintf(buffer, "\01INTL %d:%d/%d %d:%d/%d\n",
  559.         m->to.zone, m->to.net, m->to.node,
  560.         m->from.zone, m->from.net, m->from.node);
  561.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  562.   } /* if */
  563.  
  564.   if (((m->to.domain != NULL) && (m->from.domain != NULL)) &&
  565.        (stricmp(m->to.domain,m->from.domain))) {
  566.     sprintf(buffer, "\01DOMAIN %s %d:%d/%d %s %d:%d/%d\n",
  567.         strupr(m->to.domain),m->to.zone, m->to.net, m->to.node,
  568.         strupr(m->from.domain),m->from.zone, m->from.net, m->from.node);
  569.     (void)insertline(&msgbuf, msgbuf.first, buffer);
  570.   } /* if */
  571. #endif
  572.  
  573.   addid(m, ofrom /*, origtext*/ );        /* add id lines */
  574.  
  575. /* Here the IFNA TOPT/FMPT kludge lines are added, in front */
  576.  
  577. #ifdef TOFMPT
  578.   if (arealist[area].netmail) {
  579.     if (m->from.point) {
  580.       sprintf(buffer,"\01FMPT %d\n", m->from.point);
  581.       (void)insertline(&msgbuf, msgbuf.first, buffer);
  582.     } /* if */
  583.  
  584.     if (m->to.point) {
  585.       sprintf(buffer,"\01TOPT %d\n", m->to.point);
  586.       (void)insertline(&msgbuf, msgbuf.first, buffer);
  587.     } /* if */
  588.   } /* if */
  589. #endif
  590.  
  591. /* write out the lines */
  592.  
  593.     /* global msgbuf */
  594.   for (t = msgbuf.first;  (t != NULL);  t = t->next) {
  595.     char *d = buffer;
  596.     char *s = t->text;
  597.     if (s == NULL) continue;
  598.  
  599.     while (*s != EOS) {        /* write the message text to the buffer */
  600.       if (*s == '\n') {
  601.     *d++ = '\r';
  602.      /* *d++ = '\n'; */        /* linefeed after CR */
  603.       } else {
  604.     *d++ = *s;
  605.       } /* else */
  606.       s++;
  607.     } /* while */
  608.  
  609.     if (arealist[area].softcr && (strchr(t->text,'\n') == NULL))
  610.       *d++ = 0x8d;
  611.  
  612.     *d = EOS;
  613.  
  614.     tputs(buffer, &textbuf);        /* write the line */
  615.   } /* for */
  616.  
  617. /* add: check here if memory overflowed during message text building */
  618.  
  619. /* now write the header and text of the message */
  620.  
  621.   writebase(&m->header,arealist[area].board,textbuf,
  622.         arealist[area].echomail,arealist[area].netmail);
  623.  
  624.   arealist[area].new = 1;
  625.   result = TRUE;
  626.  
  627.   if (textbuf != NULL)
  628.     ptrfree(textbuf);
  629.  
  630.   if (curmsg(CurBoard) == 0)
  631.     setcur(CurBoard,m->header.msgnum);
  632.  
  633. #ifdef PARANOID
  634.   flushmsgbase();
  635. #endif
  636.  
  637.   return(result);
  638.  
  639. } /* writemsg */
  640.  
  641. /*
  642.  * Return current number of characters in buffer or -1 if error
  643.  */
  644.  
  645. static int tputs(char *s, char **buf)
  646. {
  647.   int blen, slen;
  648.  
  649.   if (s == NULL)
  650.     if (*buf == NULL)
  651.       return(0);
  652.     else
  653.       return(strlen(*buf));
  654.  
  655.   slen = strlen(s);
  656.  
  657.   if (*buf == NULL) {            /* allocate first piece of memory */
  658.     if ((*buf = malloc(slen+1)) == NULL)
  659.       return(-1);            /* no memory available */
  660.     strcpy(*buf,s);
  661.     return(slen);
  662.   } /* if */
  663.  
  664.   blen = strlen(*buf);            /* current length */
  665.  
  666.   if ((*buf = realloc(*buf, blen+slen+1)) == NULL)
  667.     return(-1);                /* allocate more bytes */
  668.  
  669.   strcat(*buf,s);            /* add to end */
  670.  
  671.   return(blen+slen);            /* new length */
  672.  
  673. } /* tputs */
  674.  
  675. /*
  676.  * Look for a user in USERS.BBS
  677.  * If 'create' is TRUE, add the user
  678.  * Return user number from 0 or -1 if error or not found
  679.  */
  680.  
  681. int searchuser(char *name, BOOLEAN create)
  682. {
  683.   USERRECORD user;
  684.   int uf;
  685.   int count;
  686.   BOOLEAN found = FALSE;
  687.   int access = O_RDWR|O_BINARY;
  688.  
  689.   if (name == NULL)
  690.     return(-1);
  691.  
  692.   if (create)
  693.     access |= O_CREAT;
  694.  
  695.   if ((uf = open(expandbbs("users.bbs"),access,S_IREAD|S_IWRITE)) == -1)
  696.     return(-1);
  697.  
  698.   count = -1;
  699.   while (!found && (eof(uf) == 0)) {
  700.     read(uf,(char *)&user,sizeof(user));
  701.     p2c_strn(user.name,35);
  702.     found = (stricmp(user.name,name) == 0);
  703.     count++;
  704.   } /* while */
  705.  
  706.   if (!found && create) {
  707.     memset(&user,0,sizeof(user));
  708.     strncpy(user.name,name,35);
  709.     c2p_str(user.name);
  710.     strcpy(user.pwd,"*no password*");
  711.     c2p_str(user.pwd);
  712.     timestr(user.lasttime,user.lastdate);
  713.     c2p_str(user.lasttime);
  714.     c2p_str(user.lastdate);
  715.     user.attrib = U_NOKILL;
  716.     if (write(uf,(char *)&user,sizeof(user)) == sizeof(user)) {
  717.       found = TRUE;
  718.       count++;
  719.     } /* if */
  720.   } /* if */
  721.  
  722.   highmsgread = user.highmsgread;    /* set global */
  723.  
  724.   close(uf);
  725.  
  726.   if (found)
  727.     return(count);
  728.   else
  729.     return(-1);
  730. } /* searchuser */
  731.  
  732. BOOLEAN updatehigh(int usernum, int high)
  733. {
  734.   USERRECORD user;
  735.   int uf;
  736.  
  737.   if ((uf = open(expandbbs("users.bbs"),O_RDWR|O_BINARY)) == -1)
  738.     return FALSE;
  739.  
  740.   if ((lseek(uf,(long)usernum*sizeof(user),SEEK_SET)) == -1) {
  741.     close(uf);
  742.     return FALSE;
  743.   } /* if */
  744.  
  745.   read(uf,(char *)&user,sizeof(user));
  746.   if (high > user.highmsgread) {
  747.     user.highmsgread = high;
  748.     lseek(uf,(long)usernum*sizeof(user),SEEK_SET);
  749.     write(uf,(char *)&user,sizeof(user));
  750.   } /* if */
  751.  
  752.   close(uf);
  753.  
  754.   return TRUE;
  755. } /* updatehigh */
  756.